%include "boot.inc"
section LOADER vstart=LOADER_BASE
LOADER_STACK_TOP equ LOADER_BASE
;	jmp loader_start

GDT_BASE: dd 0x00000000
	  dd 0x00000000
CODE_DESC: dd 0x0000ffff
	   dd DESC_CODE_HIGH4 ;代码段描述符
DATA_STACK_DESC: dd 0x0000ffff
	 	 dd DESC_DATA_HIGH4 ;数据段描述符
VIDEO_DESC: dd 0x80000007
	    dd DESC_VIDEO_HIGH4 ;显存段描述符

GDT_SIZE equ $-GDT_BASE
GDT_LIMIT equ GDT_SIZE-1

	times 60 dq 0 ; dq表示64位8字节

SELECTOR_CODE equ (0x0001<<3)+TI_GDT+RPL0 ;段选择子
SELECTOR_DATA equ (0x0002<<3)+TI_GDT+RPL0
SELECTOR_VIDEO equ (0x0003<<3)+TI_GDT+RPL0

total_mem_bytes dd 0

gdt_ptr dw GDT_LIMIT
	dd GDT_BASE

;loadermsg db '2 loader in real.'

ards_buf times 244 db 0
ards_nr dw 0

loader_start:

;---------检索内存大小----------
	xor ebx, ebx
	mov edx, 0x534d4150
	mov di, ards_buf
.e820_mem_get_loop:
	mov eax, 0x0000e820 ;int 0x15 中断功能号 e820
	mov ecx, 20
	int 0x15
	jc .e820_mem_get_failed  ;出错跳转

	add di, cx
	inc word [ards_nr]
	cmp ebx, 0
	jnz .e820_mem_get_loop
	
	mov cx, [ards_nr]
	mov ebx, ards_buf
	xor edx, edx
.find_max_mem_area: ; 寻找最大的值
	mov eax, [ebx]
	add eax, [ebx+8]
	add ebx, 20
	cmp edx, eax
	jge .next_ards
	mov edx, eax
.next_ards:
	loop .find_max_mem_area
	jmp .mem_get_ok

.e820_mem_get_failed:
	mov byte [gs:0], 'f'
	mov byte [gs:2], 'a'
	mov byte [gs:4], 'i'
	mov byte [gs:6], 'l'
	mov byte [gs:8], 'e'
	mov byte [gs:10], 'd'
	; 内存检测失败，不再继续向下执行
	jmp $

.mem_get_ok:
	mov [total_mem_bytes], edx

	;打开a20地址线
	in al, 0x92
	or al, 00000010b
	out 0x92, al
	
	;加载gdtr
	lgdt [gdt_ptr]

	;将PE位置1 打开保护模式
	mov eax, cr0
	or eax, 0x00000001
	mov cr0, eax

	;刷新流水线 跳转
	jmp dword SELECTOR_CODE:p_mode_start

[bits 32]
p_mode_start:
	mov ax, SELECTOR_DATA
	mov ds, ax

	mov es, ax
	mov ss, ax

	mov esp, LOADER_STACK_TOP
	mov ax, SELECTOR_VIDEO
	mov gs, ax

	mov eax, KERNEL_START_SECTOR ; 内核所在的扇区地址
	mov ebx, KERNEL_BIN_BASE_ADDR ; 将内核写到内存的地址
	mov ecx, 200 ; 读取200个扇区
	call rd_disk_m_32 ; 读取内核到内存	

	call setup_page

	sgdt [gdt_ptr]
	mov ebx, [gdt_ptr+2]
	or dword [ebx+0x18+4], 0xc0000000
	add dword [gdt_ptr+2], 0xc0000000
	add esp, 0xc0000000
	mov eax, PAGE_DIR_TABLE_POS
	mov cr3, eax ; 将pde页目录表地址存如cr3中
	mov eax, cr0
	or eax, 0x80000000 ;cr0寄存器31位PG位，置1
	mov cr0, eax ; 开启分页
	lgdt [gdt_ptr]

	;mov byte [gs:160], 'V'
	jmp SELECTOR_CODE:enter_kernel

enter_kernel:
	call kernel_init
	mov esp, 0xc009f000
	jmp KERNEL_ENTRY_POINT

	jmp $

;-----------创建页目录(pdt)和页表(ptt)

setup_page:
	;将页目录表占用的空间清零
	mov ecx, 4096
	mov esi, 0
.clear_page_dir:
	mov byte [PAGE_DIR_TABLE_POS+esi], 0
	inc esi
	loop .clear_page_dir


.create_pde:
	mov eax, PAGE_DIR_TABLE_POS ; 0x0010 0000
	add eax, 0x1000 ; 0x0010 1000为第一个页表地址   0001 0000 0000 0000
	mov ebx, eax
	

	or eax, PG_US_U|PG_RW_RW|PG_P
	mov [PAGE_DIR_TABLE_POS+0x0], eax ; 第一个页目录项pde = 第一个页表地址
	mov [PAGE_DIR_TABLE_POS+0xc00], eax ; 3g-4g 地址
	sub eax, 0x1000
	mov [PAGE_DIR_TABLE_POS + 4092], eax ; 最后的页目录项pde = 开始地址

;创建页表项
	mov ecx, 256 ; 低1m, 1m/4k = 256,为内核页表项
	mov esi, 0
	mov edx, PG_US_U|PG_RW_RW|PG_P ; 0x7
.create_pte:
	mov [ebx+esi*4], edx ; 向页表中写入页表项pte
	add edx, 0x1000 ; 0x1007 后12位为属性
	inc esi
	loop .create_pte

; 创建其他页表的页目录项(pde)
	mov eax, PAGE_DIR_TABLE_POS
	add eax, 0x2000 ; 0x00102000 从第二个页表的地址
	or eax, PG_US_U|PG_RW_RW|PG_P
	mov ebx, PAGE_DIR_TABLE_POS
	mov ecx, 254
	mov esi, 769 ; 范围是769-1022
.create_kernel_pde:
	mov [ebx+esi*4], eax
	inc esi
	add eax, 0x1000
	loop .create_kernel_pde
	ret

rd_disk_m_32:
	mov esi, eax
	mov di, cx

	mov dx, 0x1f2 ;设置读取区块数量
	mov al, cl
	out dx, al

	mov eax, esi

	mov dx, 0x1f3 ;low lba低8位地址
	out dx, al


	mov cl, 8
	shr eax, cl ; 右移8位
	mov dx, 0x1f4 ;mid 中8位
	out dx, al

	shr eax, cl
	mov dx, 0x1f5 ;high 高8位
	out dx, al

	shr eax, cl
	and al, 0x0f ; 0000 1111 低四位表示lba第25-28位
	or al, 0xe0 ; 1110 0000 5:0表示主盘, 6:1未知, 7:1为lba模式, 8:1未知
	mov dx, 0x1f6
	out dx, al
	
	mov dx, 0x1f7 ;0x20表示读取，0x30表示写入
	mov al, 0x20
	out dx, al

.not_ready:
	nop
	in al, dx
	and al, 0x88 ; 1000 1000
	cmp al, 0x08 ; 0000 1000 比较al和0x08如果相等设ZF为1
	jnz .not_ready ; 判断标准寄存器ZF是否等于0,不为零跳转
	
	mov ax, di
	mov dx, 256 ;一次读取两字节读取次数，读取扇区数*512\2
	mul dx ; ax*dx,低8位放ax中,高8位在dx
	mov cx, ax
	mov dx, 0x1f0
.go_on_read:
	in ax, dx ;读取两个字节到ax
	mov [ds:ebx], ax
	add ebx, 2
	loop .go_on_read
	ret

kernel_init:
	xor eax, eax
	xor ebx, ebx
	xor ecx, ecx
	xor edx, edx

	mov dx, [KERNEL_BIN_BASE_ADDR+42]
	mov ebx, [KERNEL_BIN_BASE_ADDR+28]
	add ebx, KERNEL_BIN_BASE_ADDR
	mov cx, [KERNEL_BIN_BASE_ADDR+44]
.each_segment:
	cmp byte [ebx+0], PT_NULL
	je .PTNULL
	push dword [ebx+16]
	mov eax, [ebx+4]
	add eax, KERNEL_BIN_BASE_ADDR
	push eax
	push dword [ebx+8]
	call mem_cpy
	add esp, 12
.PTNULL:
	add ebx, edx
	loop .each_segment
	ret

mem_cpy:
	cld
	push ebp
	mov ebp, esp
	push ecx

	mov edi, [ebp+8]
	mov esi, [ebp+12]
	mov ecx, [ebp+16]
	rep movsb

	pop ecx
	pop ebp
	ret






